﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CodeBuilder.Models.TemplateModels;
using CodeBuilder.Templates;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using RazorEngine.Configuration;
using RazorEngine.Templating;
using Engine = RazorEngine.Engine;
using Task = System.Threading.Tasks.Task;

namespace CodeBuilder
{
    public partial class CommandBase
    {
        #region 代码生成

        /// <summary>
        /// 获取DtoModel
        /// </summary>
        /// <param name="applicationStr"></param>
        /// <param name="name"></param>
        /// <param name="dirName"></param>
        /// <param name="codeClass"></param>
        /// <returns></returns>
        protected DtoFileModel GetDtoModel(string applicationStr, string name, string cnName, string description, string dirName, CodeClass codeClass)
        {
            var model = new DtoFileModel() { Namespace = applicationStr, Name = name, CnName = cnName, Description = description, DirName = dirName.Replace("\\", ".") };
            List<ClassProperty> classProperties = new List<ClassProperty>();

            var codeMembers = codeClass.Members;
            AddClassProperty(codeMembers, ref classProperties);

            model.KeyType = "int";
            if (codeClass.Bases.Count > 0)
            {
                foreach (CodeElement2 elem in codeClass.Bases)
                {
                    if (elem.FullName.IndexOf("System.Int64") > -1) model.KeyType = "long";
                    if (elem.FullName.IndexOf("System.Guid") > -1) model.KeyType = "Guid";
                    if (elem.FullName.IndexOf("System.String") > -1) model.KeyType = "string";
                }
                //C#仅支持单继承
                //CodeElements baseMembers = codeClass.Bases.Cast<CodeClass2>().ToList()[0].Members;
                //AddClassProperty(baseMembers, ref classProperties);
            }

            model.ClassPropertys = classProperties;

            return model;
        }

        private void AddClassProperty(CodeElements codeMembers, ref List<ClassProperty> classProperties)
        {
            List<ClassAttribute> classAttributes = null;

            foreach (CodeElement codeMember in codeMembers)
            {
                if (codeMember.Kind == vsCMElement.vsCMElementProperty)
                {
                    ClassProperty classProperty = new ClassProperty();
                    CodeProperty property = codeMember as CodeProperty;
                    classProperty.Name = property.Name;
                    //获取属性类型
                    var propertyType = property.Type;
                    switch (propertyType.TypeKind)
                    {
                        case vsCMTypeRef.vsCMTypeRefString:
                            classProperty.PropertyType = "string";
                            break;

                        case vsCMTypeRef.vsCMTypeRefInt:
                            classProperty.PropertyType = "int";
                            break;

                        case vsCMTypeRef.vsCMTypeRefLong:
                            classProperty.PropertyType = "long";
                            break;

                        case vsCMTypeRef.vsCMTypeRefByte:
                            classProperty.PropertyType = "byte";
                            break;

                        case vsCMTypeRef.vsCMTypeRefChar:
                            classProperty.PropertyType = "char";
                            break;

                        case vsCMTypeRef.vsCMTypeRefShort:
                            classProperty.PropertyType = "short";
                            break;

                        case vsCMTypeRef.vsCMTypeRefBool:
                            classProperty.PropertyType = "bool";
                            break;

                        case vsCMTypeRef.vsCMTypeRefDecimal:
                            classProperty.PropertyType = "decimal";
                            break;

                        case vsCMTypeRef.vsCMTypeRefFloat:
                            classProperty.PropertyType = "float";
                            break;

                        case vsCMTypeRef.vsCMTypeRefDouble:
                            classProperty.PropertyType = "double";
                            break;

                        default:
                            classProperty.PropertyType = propertyType.AsFullName;
                            break;
                    }

                    string propertyCnName = "";//属性中文名称

                    classAttributes = new List<ClassAttribute>();
                    //获取属性特性
                    foreach (CodeAttribute codeAttribute in property.Attributes)
                    {
                        ClassAttribute classAttribute = new ClassAttribute();
                        if (codeAttribute.Name == "Required")
                        {
                            classAttribute.NameValue = "[Required]";

                            classAttribute.Name = "Required";
                            classAttribute.Value = "true";
                        }
                        else
                        {
                            classAttribute.NameValue = "[" + codeAttribute.Name + "(" + codeAttribute.Value + ")]";
                            classAttribute.Name = codeAttribute.Name;
                            classAttribute.Value = codeAttribute.Value;

                            if (codeAttribute.Name == "Display")
                            {
                                propertyCnName = codeAttribute.Value.Replace("Name = ", "").Replace("\"", "");
                            }
                        }
                        classAttributes.Add(classAttribute);
                    }

                    classProperty.CnName = string.IsNullOrEmpty(propertyCnName) ? property.Name : propertyCnName;
                    classProperty.ClassAttributes = classAttributes;

                    classProperties.Add(classProperty);
                }
            }
        }

        #region 生成后端
        /// <summary>
        /// 创建Permissions权限常量类
        /// </summary>
        /// <param name="applicationStr">根命名空间</param>
        /// <param name="name">类名</param>
        /// <param name="authorizationFolder">父文件夹</param>
        protected void CreatePermissionFile(string applicationStr, string name, ProjectItem authorizationFolder)
        {
            var model = new PermissionsFileModel() { Namespace = applicationStr, Name = name };
            string content = Engine.Razor.RunCompile("Server.Core.Authoriztion.PermissionsTemplate", typeof(PermissionsFileModel), model);
            string fileName = $"{name}Permissions.cs";
            AddFileToProjectItem(authorizationFolder, content, fileName);
        }

        /// <summary>
        /// 创建AppAuthorizationProvider权限配置类
        /// </summary>
        /// <param name="applicationStr">根命名空间</param>
        /// <param name="name">类名</param>
        /// <param name="authorizationFolder">父文件夹</param>
        protected void CreateAppAuthorizationProviderFile(string applicationStr, string name, string cnName, ProjectItem authorizationFolder)
        {
            var model = new AppAuthorizationProviderFileModel() { Namespace = applicationStr, Name = name, CnName = cnName };
            string content = Engine.Razor.RunCompile("Server.Core.Authoriztion.AppAuthorizationProviderTemplate", typeof(AppAuthorizationProviderFileModel), model);
            string fileName = name + "AuthorizationProvider.cs";
            AddFileToProjectItem(authorizationFolder, content, fileName);
        }

        /// <summary>
        /// 添加本地化
        /// </summary>
        /// <param name="topProject"></param>
        /// <param name="className"></param>
        protected void SetLocalization(string applicationStr, string name, string cnName, Project topProject)
        {
            var baseFileName = applicationStr.Split('.').Length > 1 ? applicationStr.Split('.')[1] : applicationStr;
            string localizationSourcePath = $"{topProject.FileName.Substring(0, topProject.FileName.LastIndexOf("\\"))}\\Localization\\SourceFiles";
            string zhHansLocalization = Path.Combine(localizationSourcePath, $"{baseFileName}-zh-Hans.xml");
            string zhHansContent = File.ReadAllText(zhHansLocalization);
            zhHansContent = zhHansContent.Replace("</texts>", $"  <text name=\"{name}\">{cnName}</text>\r\n  </texts>");
            File.WriteAllText(zhHansLocalization, zhHansContent, System.Text.Encoding.UTF8);

            string enLocalization = Path.Combine(localizationSourcePath, $"{baseFileName}.xml");
            string enContent = File.ReadAllText(enLocalization);
            enContent = enContent.Replace("</texts>", $"  <text name=\"{name}\">{name}</text>\r\n  </texts>");
            File.WriteAllText(enLocalization, enContent, System.Text.Encoding.UTF8);
        }

        /// <summary>
        /// 添加权限
        /// </summary>
        /// <param name="topProject"></param>
        /// <param name="className"></param>
        protected void SetPermission(Project topProject, string className)
        {
            ProjectItem AppAuthorizationProviderProjectItem = _dte.Solution.FindProjectItem(topProject.FileName.Substring(0, topProject.FileName.LastIndexOf("\\")) + "\\Authorization\\AppAuthorizationProvider.cs");
            if (AppAuthorizationProviderProjectItem != null)
            {
                CodeClass codeClass = GetClass(AppAuthorizationProviderProjectItem.FileCodeModel.CodeElements);
                var codeChilds = codeClass.Members;
                foreach (CodeElement codeChild in codeChilds)
                {
                    if (codeChild.Kind == vsCMElement.vsCMElementFunction && codeChild.Name == "SetPermissions")
                    {
                        var iStr = "            Set" + className + "Permissions(pages);\r\n\r\n";
                        var insertCode = codeChild.GetEndPoint(vsCMPart.vsCMPartBody).CreateEditPoint();
                        insertCode.Insert(iStr);
                    }
                }
                AppAuthorizationProviderProjectItem.Save();
            }
        }


        protected void CreateEntityMaperFile(DtoFileModel model, string name, ProjectItem dtoFolder)
        {
            string content_Edit = Engine.Razor.RunCompile("Server.EntityframeworkCore.EntityMapper.CfgTemplate", typeof(DtoFileModel), model);
            string fileName_Edit = $"{name}Cfg.cs";
            AddFileToProjectItem(dtoFolder, content_Edit, fileName_Edit);
        }

        /// <summary>
        /// 创建Dto类
        /// </summary>
        /// <param name="model"></param>
        /// <param name="name"></param>
        /// <param name="dtoFolder"></param>
        protected void CreateDtoFile(DtoFileModel model, string name, ProjectItem dtoFolder)
        {
            string content_Edit = Engine.Razor.RunCompile("Server.Application.Dto.EditDtoTemplate", typeof(DtoFileModel), model);
            string fileName_Edit = $"{name}EditDto.cs";
            AddFileToProjectItem(dtoFolder, content_Edit, fileName_Edit);

            string content_List = Engine.Razor.RunCompile("Server.Application.Dto.ListDtoTemplate", typeof(DtoFileModel), model);
            string fileName_List = $"{name}ListDto.cs";
            AddFileToProjectItem(dtoFolder, content_List, fileName_List);

            string content_CreateAndUpdate = Engine.Razor.RunCompile("Server.Application.Dto.CreateOrUpdateInputDtoTemplate", typeof(DtoFileModel), model);
            string fileName_CreateAndUpdate = $"CreateOrUpdate{name}Input.cs";
            AddFileToProjectItem(dtoFolder, content_CreateAndUpdate, fileName_CreateAndUpdate);

            string content_GetForUpdate = Engine.Razor.RunCompile("Server.Application.Dto.GetForEditOutputDtoTemplate", typeof(DtoFileModel), model);
            string fileName_GetForUpdate = $"Get{name}ForEditOutput.cs";
            AddFileToProjectItem(dtoFolder, content_GetForUpdate, fileName_GetForUpdate);

            string content_GetsInput = Engine.Razor.RunCompile("Server.Application.Dto.GetsInputTemplate", typeof(DtoFileModel), model);
            string fileName_GetsInput = $"Get{name}sInput.cs";
            AddFileToProjectItem(dtoFolder, content_GetsInput, fileName_GetsInput);

            string content_Profile = Engine.Razor.RunCompile("Server.Application.Dto.DtoProfileTemplate", typeof(DtoFileModel), model);
            string fileName_Profile = $"{name}MapProfile.cs";
            AddFileToProjectItem(dtoFolder, content_Profile, fileName_Profile);
        }

        /// <summary>
        /// 创建Service类
        /// </summary>
        /// <param name="applicationStr">根命名空间</param>
        /// <param name="name">类名</param>
        /// <param name="dtoFolder">父文件夹</param>
        /// <param name="dirName">类所在文件夹目录</param>
        protected void CreateServiceFile(string applicationStr, string name, string cnName, ProjectItem dtoFolder, string dirName, string keyType)
        {
            var model = new ServiceFileModel() { Namespace = applicationStr, Name = name, CnName = cnName, DirName = dirName.Replace("\\", "."), KeyType = keyType };

            string content_IService = Engine.Razor.RunCompile("Server.Application.IServiceTemplate", typeof(ServiceFileModel), model);
            string fileName_IService = $"I{name}AppService.cs";
            AddFileToProjectItem(dtoFolder, content_IService, fileName_IService);

            string content_Service = Engine.Razor.RunCompile("Server.Application.ServiceTemplate", typeof(ServiceFileModel), model);
            string fileName_Service = $"{name}AppService.cs";
            AddFileToProjectItem(dtoFolder, content_Service, fileName_Service);
        }

        /// <summary>
        /// 添加DbSet到DbContext
        /// </summary>
        /// <param name="topProject"></param>
        /// <param name="className"></param>
        protected void SetDbSetToDbContext(Project entityFrameworkProject, string applicationStr, string namespaceStr, string className)
        {
            var baseDbContentStr = applicationStr.Split('.')[applicationStr.Split('.').Length - 1];
            ProjectItem customDbContextProviderProjectItem = _dte.Solution.FindProjectItem(entityFrameworkProject.FileName.Substring(0, entityFrameworkProject.FileName.LastIndexOf("\\")) + $"\\EntityFrameworkCore\\{baseDbContentStr}DbContext.cs");
            if (customDbContextProviderProjectItem != null)
            {
                CodeClass codeClass = GetClass(customDbContextProviderProjectItem.FileCodeModel.CodeElements);
                var codeMembers = codeClass.Members;
                foreach (CodeElement codeMember in codeMembers)
                {
                    if (codeMember.Kind == vsCMElement.vsCMElementFunction && codeMember.Name == "OnModelCreating")
                    {
                        var iStr = $"            modelBuilder.ApplyConfiguration(new {className}Cfg());\r\n\r\n";
                        var insertCode = codeMember.GetEndPoint(vsCMPart.vsCMPartBody).CreateEditPoint();
                        insertCode.Insert(iStr);
                    }
                }

                var codeChilds = codeClass.Collection;
                foreach (CodeElement codeChild in codeChilds)
                {
                    var iStr = "        public DbSet<" + namespaceStr + "." + className + "> " + className + "s { get; set; }\r\n";
                    var insertCode = codeChild.GetEndPoint(vsCMPart.vsCMPartBody).CreateEditPoint();
                    insertCode.Insert(iStr);
                }

                customDbContextProviderProjectItem.Save();
            }
        }

        /// <summary>
        /// 编辑CustomDtoMapper.cs,添加映射
        /// </summary>
        /// <param name="applicationProject"></param>
        /// <param name="className"></param>
        /// <param name="classCnName"></param>
        protected void SetMapper(Project applicationProject, string className, string classCnName)
        {
            ProjectItem customDtoMapperProjectItem = _dte.Solution.FindProjectItem(applicationProject.FileName.Substring(0, applicationProject.FileName.LastIndexOf("\\")) + "\\CustomDtoMapper.cs");
            if (customDtoMapperProjectItem != null)
            {
                CodeClass codeClass = GetClass(customDtoMapperProjectItem.FileCodeModel.CodeElements);
                var codeChilds = codeClass.Members;
                foreach (CodeElement codeChild in codeChilds)
                {
                    if (codeChild.Kind == vsCMElement.vsCMElementFunction && codeChild.Name == "CreateMappings")
                    {
                        var insertCode = codeChild.GetEndPoint(vsCMPart.vsCMPartBody).CreateEditPoint();
                        insertCode.Insert("            // " + classCnName + "\r\n");
                        insertCode.Insert("            configuration.CreateMap<" + className + ", " + className + "EditDto>();\r\n");
                        insertCode.Insert("            configuration.CreateMap<" + className + ", " + className + "ListDto>();\r\n");
                        insertCode.Insert("            configuration.CreateMap<" + className + "EditDto, " + className + ">();\r\n");
                        insertCode.Insert("            configuration.CreateMap<" + className + "ListDto, " + className + ">();\r\n");
                        insertCode.Insert("\r\n");
                    }
                }

                customDtoMapperProjectItem.Save();
            }
        }

        #endregion


        #endregion
    }
}
